#include "oslib/osfile.h"

#include "flexlib/flex.h"

#include "MemFile.h"

#define MEMFILE_BLOCK        (4096)
#define CHAR_BASE            ('A')


//////////////////////////////////////////////////////////////////
// Save a section name
void SaveSection (FILE * pfhFile, char * szSection)
{
  fputc ('\n', pfhFile);
  fputc ('[', pfhFile);
  fputs (szSection, pfhFile);
  fputc (']', pfhFile);
  fputc ('\n', pfhFile);
}

//////////////////////////////////////////////////////////////////
// Save a section name to memory
void SaveSectionMem (MemFile * pfhFile, char * szSection)
{
  memputc ('\n', pfhFile);
  memputc ('[', pfhFile);
  memputs (szSection, pfhFile);
  memputc (']', pfhFile);
  memputc ('\n', pfhFile);
}

//////////////////////////////////////////////////////////////////
// Save details line
void SaveDetail (FILE * pfhFile, char * szName, char * szDetails)
{
  int                         nLength;

  fputs (szName, pfhFile);
  fputc (' ', pfhFile);
  fputc (':', pfhFile);
  fputc (' ', pfhFile);
  nLength = 0;
  while (szDetails[nLength] >= 0x20)
  {
    nLength++;
  }
  szDetails[nLength] = 0;
  fputs (szDetails, pfhFile);
  fputc ('\n', pfhFile);
}

//////////////////////////////////////////////////////////////////
// Save details line to memory
void SaveDetailMem (MemFile * pfhFile, char * szName, char * szDetails)
{
  int                         nLength;

  memputs (szName, pfhFile);
  memputc (' ', pfhFile);
  memputc (':', pfhFile);
  memputc (' ', pfhFile);
  nLength = 0;
  while (szDetails[nLength] >= 0x20)
  {
    nLength++;
  }
  szDetails[nLength] = 0;
  memputs (szDetails, pfhFile);
  memputc ('\n', pfhFile);
}

//////////////////////////////////////////////////////////////////
// Find the specified section and return its file position
int FindSection (FILE * pfhFile, char * szSection)
{
  char                        szString[1024];
  char                        *szReturn;
  int                         nReturn;
  char                        *szEnd;

  fseek (pfhFile, 0, SEEK_SET);

  nReturn = EOF;

  do
  {
    do
    {
      szReturn = fgets (szString, sizeof (szString), pfhFile);
    } while (szReturn && (szString[0] != '['));

    if (szReturn)
    {
      szEnd = strchr (szString, ']');
      if (szEnd)
      {
        szEnd[0] = 0;
        if (strcmp (szSection, (szString + 1)) == 0)
        {
          nReturn = ftell (pfhFile);
        }
      }
    }
  } while (szReturn && (nReturn == EOF));

  return nReturn;
}

//////////////////////////////////////////////////////////////////
// Find the specified section and return its memory position
int FindSectionMem (MemFile * pfhFile, char * szSection)
{
  char                        szString[1024];
  char                        *szReturn;
  int                         nReturn;
  char                        *szEnd;

  memseek (pfhFile, 0, SEEK_SET);

  nReturn = EOF;

  do
  {
    do
    {
      szReturn = memgets (szString, sizeof (szString), pfhFile);
    } while (szReturn && (szString[0] != '['));

    if (szReturn)
    {
      szEnd = strchr (szString, ']');
      if (szEnd)
      {
        szEnd[0] = 0;
        if (strcmp (szSection, (szString + 1)) == 0)
        {
          nReturn = memtell (pfhFile);
        }
      }
    }
  } while (szReturn && (nReturn == EOF));

  return nReturn;
}

//////////////////////////////////////////////////////////////////
// Find the value of the specified variable
bool FindValue (FILE * pfhFile, int nSection, char * szName, char * szValue, int nValueLen)
{
  char                        szString[1024];
  char                        *szReturn;
  bool                        boReturn = FALSE;
  char                        *szEnd;
  char                        *szStart;
  int                         nPos;
  int                         nNameLen;

  fseek (pfhFile, nSection, SEEK_SET);

  nNameLen = strlen (szName);

  do
  {
    szReturn = fgets (szString, sizeof (szString), pfhFile);

    if (szReturn)
    {
      if (szReturn[0] == '[')
      {
        szReturn = NULL;
      }
      else
      {
        nPos = 0;
        while ((nPos < ((int)sizeof (szString) - 1))
          && (szReturn[nPos] >= 0x20))
        {
          nPos++;
        }
        szReturn[nPos] = 0;

        szStart = strchr (szString, ':');
        if (szStart > szString)
        {
          szEnd = szStart - 1;
          while ((szEnd > szString) && (szEnd[0] == ' '))
          {
            szEnd--;
          }

          if ((nNameLen == (szEnd - szString + 1))
            && (strncmp (szName, szString, nNameLen) == 0))
          {
            szStart++;
            while (szStart[0] == ' ')
            {
              szStart++;
            }
            strncpy (szValue, szStart, nValueLen);
            boReturn = TRUE;
          }
        }
      }
    }
  } while ((szReturn) && (!boReturn));

  if (!boReturn)
  {
    szValue[0] = 0;
  }

  return boReturn;
}

//////////////////////////////////////////////////////////////////
// Find the value of the specified variable from memory
bool FindValueMem (MemFile * pfhFile, int nSection, char * szName, char * szValue, int nValueLen)
{
  char                        szString[1024];
  char                        *szReturn;
  bool                        boReturn = FALSE;
  char                        *szEnd;
  char                        *szStart;
  int                         nPos;
  int                         nNameLen;

  memseek (pfhFile, nSection, SEEK_SET);

  nNameLen = strlen (szName);

  do
  {
    szReturn = memgets (szString, sizeof (szString), pfhFile);

    if (szReturn)
    {
      if (szReturn[0] == '[')
      {
        szReturn = NULL;
      }
      else
      {
        nPos = 0;
        while ((nPos < ((int)sizeof (szString) - 1))
          && (szReturn[nPos] >= 0x20))
        {
          nPos++;
        }
        szReturn[nPos] = 0;

        szStart = strchr (szString, ':');
        if (szStart > szString)
        {
          szEnd = szStart - 1;
          while ((szEnd > szString) && (szEnd[0] == ' '))
          {
            szEnd--;
          }

          if ((nNameLen == (szEnd - szString + 1))
            && (strncmp (szName, szString, nNameLen) == 0))
          {
            szStart++;
            while (szStart[0] == ' ')
            {
              szStart++;
            }
            strncpy (szValue, szStart, nValueLen);
            boReturn = TRUE;
          }
        }
      }
    }
  } while ((szReturn) && (!boReturn));

  if (!boReturn)
  {
    szValue[0] = 0;
  }

  return boReturn;
}

//////////////////////////////////////////////////////////////////
// Open a memory 'file' for reading/writing
MemFile * memopen (char * filename, char * mode)
{
  MemFile                     *psMemFile = NULL;
  int                         nSuccess;

  if (mode[0] == 'r')
  {
    psMemFile = (MemFile*)malloc (sizeof (MemFile));

    nSuccess = LoadFileFlex (filename, & psMemFile->pcMemory,
      & psMemFile->nSize);

    if ((nSuccess == 0) || (psMemFile->pcMemory == NULL))
    {
      free (psMemFile);
      psMemFile = NULL;
      if (nSuccess == 0)
      {
        psMemFile = NULL;
//        ShowWarningTag ("Er20");
      }
    }
    else
    {
      if (psMemFile)
      {
//        if (strcmp (gszUnlockPassword, "") != 0)
//        {
//          // Attempt to unscramble the file
//          UnscrambleData ((int*)psMemFile->pcMemory,
//            (int*)psMemFile->pcMemory, ((psMemFile->nSize) / 4),
//            gszUnlockPassword);
//        }

        psMemFile->nPos = 0;
        psMemFile->cMode = mode[0];

        strncpy (psMemFile->szFilename, filename, 1024);
      }
    }
  }

  if (mode[0] == 'a')
  {
    psMemFile = (MemFile*)malloc (sizeof (MemFile));

    nSuccess = LoadFileFlex (filename, & psMemFile->pcMemory,
      & psMemFile->nSize);

    if ((nSuccess == 0) || (psMemFile->pcMemory == NULL))
    {
      free (psMemFile);
      psMemFile = NULL;
      if (nSuccess == 0)
      {
        psMemFile = NULL;
//        ShowWarningTag ("Er20");
      }
    }
    else
    {
      if (psMemFile)
      {
//        if (strcmp (gszUnlockPassword, "") != 0)
//        {
//          // Attempt to unscramble the file
//          UnscrambleData ((int*)psMemFile->pcMemory,
//            (int*)psMemFile->pcMemory, ((psMemFile->nSize) / 4),
//            gszUnlockPassword);
//        }

        psMemFile->nPos = psMemFile->nSize;
        psMemFile->cMode = 'w';

        strncpy (psMemFile->szFilename, filename, 1024);
      }
    }
  }

  if (mode[0] == 'w')
  {
    psMemFile = (MemFile*)malloc (sizeof (MemFile));

    nSuccess = flex_alloc ((flex_ptr)& psMemFile->pcMemory, MEMFILE_BLOCK);

    psMemFile->nPos = 0;
    psMemFile->cMode = mode[0];
    psMemFile->nSize = MEMFILE_BLOCK;

    strncpy (psMemFile->szFilename, filename, 1024);

    if (nSuccess == 0)
    {
      free (psMemFile);
//      ShowWarningTag ("Er21");
      psMemFile = NULL;
    }
  }

  return psMemFile;
}

//////////////////////////////////////////////////////////////////
// Close a memory 'file' for reading/writing
int memclose (MemFile * psMemFile)
{
  int                         nSuccess = EOF;
  os_error                    *psError;
  int                         nSaveSize;

  if (psMemFile->cMode == 'r')
  {
    flex_free ((flex_ptr)& psMemFile->pcMemory);
    free (psMemFile);
    nSuccess = 0;
  }

  if (psMemFile->cMode == 'w')
  {
    nSaveSize = psMemFile->nPos;

//    if (strcmp (gszUnlockPassword, "") != 0)
//    {
//      // Scramble the file
//      nSaveSize = (nSaveSize + 15) & ~15;
//      ScrambleData ((int*)psMemFile->pcMemory,
//        (int*)psMemFile->pcMemory, (nSaveSize / 4),
//        gszUnlockPassword);
//    }

    // Save out the file
    if (psMemFile->szFilename[0] != 0)
    {
      psError = xosfile_save_stamped (psMemFile->szFilename, 0xfff,
        psMemFile->pcMemory, psMemFile->pcMemory + nSaveSize);
//      err (psError);

      if (psError == NULL)
      {
        nSuccess = 0;
      }
    }

    flex_free ((flex_ptr)& psMemFile->pcMemory);
    free (psMemFile);
  }

  return nSuccess;
}

//////////////////////////////////////////////////////////////////
// Read a string in from memory, terminating on newline/null etc..
char * memgets (char * str, int n, MemFile * psMemory)
{
  int                         nPos;
  char                        *szReturn;

  if (psMemory->nPos >= psMemory->nSize)
  {
    szReturn = NULL;
  }
  else
  {
    nPos = 0;
    while ((nPos < (n - 1)) && (psMemory->nPos < psMemory->nSize)
      && (psMemory->pcMemory[psMemory->nPos] >= 0x20))
    {
      str[nPos] = psMemory->pcMemory[psMemory->nPos];
      nPos++;
      psMemory->nPos++;
    }
    str[nPos] = 0;
    psMemory->nPos++;

    szReturn = str;
  }

  return szReturn;
}

//////////////////////////////////////////////////////////////////
// Set the 'file' position in memory
int memseek (MemFile * psMemory, int pos, int from)
{
  int                         nReturn = 0;

  switch (from)
  {
    case SEEK_SET:
      psMemory->nPos = pos;
      break;
    case SEEK_CUR:
      psMemory->nPos += pos;
      break;
    case SEEK_END:
      psMemory->nPos += psMemory->nSize - pos;
      break;
    default:
      nReturn = 1;
      break;
  }

  if (psMemory->nPos < 0)
  {
    psMemory->nPos = 0;
    nReturn = 1;
  }
  if (psMemory->nPos >= psMemory->nSize)
  {
    psMemory->nPos = psMemory->nSize;
    nReturn = 1;
  }

  return nReturn;
}

//////////////////////////////////////////////////////////////////
// Read the 'file' position in memory
int memtell (MemFile * psMemory)
{
  int                         nReturn;

  if (psMemory->nPos >= psMemory->nSize)
  {
    nReturn = EOF;
  }
  else
  {
    nReturn = psMemory->nPos;
  }

  return nReturn;
}

//////////////////////////////////////////////////////////////////
// Write character to memory 'file'
int memputc (int c, MemFile * psMemory)
{
  int                         nNewSize;
  int                         nSuccess = 1;
  int                         nReturn;

  if ((psMemory->nPos + 1 + 16) >= psMemory->nSize)
  {
    nNewSize = (((psMemory->nSize + 1 + 16) + (MEMFILE_BLOCK - 1))
      & ~MEMFILE_BLOCK);
    nSuccess = flex_extend ((flex_ptr)& psMemory->pcMemory, nNewSize);

    if (nSuccess == 1)
    {
      psMemory->nSize = nNewSize;
    }
  }

  if (nSuccess == 1)
  {
    (char)psMemory->pcMemory[psMemory->nPos] = c;
    psMemory->nPos += 1;
    nReturn = c;
  }
  else
  {
    nReturn = EOF;
  }

  return nReturn;
}

//////////////////////////////////////////////////////////////////
// Write string to memory 'file'
int memputs (char * str, MemFile * psMemory)
{
  int                         nLength;
  int                         nNewSize;
  int                         nSuccess = 1;
  int                         nReturn;

  nLength = strlen (str);

  if ((psMemory->nPos + nLength + 16) >= psMemory->nSize)
  {
    nNewSize = (((psMemory->nSize + nLength + 16) + (MEMFILE_BLOCK - 1))
      & ~MEMFILE_BLOCK);
    nSuccess = flex_extend ((flex_ptr)& psMemory->pcMemory, nNewSize);

    if (nSuccess == 1)
    {
      psMemory->nSize = nNewSize;
    }
  }

  if (nSuccess == 1)
  {
    strncpy (psMemory->pcMemory + psMemory->nPos, str, nLength);
    psMemory->nPos += nLength;
    nReturn = nLength;
  }
  else
  {
    nReturn = EOF;
  }

  return nReturn;
}

//////////////////////////////////////////////////////////////////
// Load a file into a block of flex memory
int LoadFileFlex (char * szFilename, char * * ppcMemory, int * pnSize)
{
  int                         nSize;
  int                         nSuccess = 0;
  fileswitch_object_type      eObjectType;

  xosfile_read_stamped_no_path (szFilename, & eObjectType, NULL, NULL,
    & nSize, NULL, NULL);

  if ((eObjectType != fileswitch_NOT_FOUND) && (nSize > 0))
  {
    if (pnSize)
    {
      *pnSize = nSize;
    }
    nSuccess = flex_alloc ((flex_ptr) ppcMemory, nSize);

    if ((nSuccess == 1) && (*ppcMemory))
    {
//      err (xosfile_load_stamped_no_path (szFilename, * ppcMemory,  NULL,
//        NULL, NULL, NULL, NULL));
      xosfile_load_stamped_no_path (szFilename, * ppcMemory,  NULL,
        NULL, NULL, NULL, NULL);
    }
    else
    {
      *ppcMemory = NULL;
    }
  }
  else
  {
    nSuccess = 1;
    *ppcMemory = NULL;
  }

  return nSuccess;
}

//////////////////////////////////////////////////////////////////
// Convert an array of numbers to a string
void HexToString (char * szPlainText, int * anBuffer, int nBufferLen)
{
  int                         nLoop;
  char                        *pcBuffer;
  int                         nLen;

  nLen = nBufferLen * 4;
  pcBuffer = (char*)anBuffer;

  for (nLoop = 0; nLoop < nLen; nLoop++)
  {
    szPlainText[nLoop * 2 + 0] =
      CHAR_BASE + ((pcBuffer[nLoop] & (15 << 0)) >> 0);
    szPlainText[nLoop * 2 + 1] =
      CHAR_BASE + ((pcBuffer[nLoop] & (15 << 4)) >> 4);
  }
  szPlainText[nLen * 2 + 0] = 0;
  szPlainText[nLen * 2 + 1] = 0;
}

//////////////////////////////////////////////////////////////////
// Convert a string to an array of numbers
void StringToHex (char * szPlainText, int * anBuffer, int nBufferLen)
{
  int                         nLoop;
  char                        *pcBuffer;
  int                         nLen;

  nLen = (strlen (szPlainText) / 2);
  pcBuffer = (char*)anBuffer;
  if (nLen > (nBufferLen * 4))
  {
    nLen = nBufferLen * 4;
  }

  for (nLoop = 0; nLoop < nLen; nLoop++)
  {
    pcBuffer[nLoop]
      = (((int)(szPlainText[nLoop * 2 + 0] - CHAR_BASE)) << 0)
      + (((int)(szPlainText[nLoop * 2 + 1] - CHAR_BASE)) << 4);
  }
}

//////////////////////////////////////////////////////////////////
// Create a memory 'file' from memory
MemFile * memcreate (char * * ppcMem, int nSize, char * mode)
{
  MemFile                     *psMemFile = NULL;
  int                         nSuccess;

  if (mode[0] == 'r')
  {
    psMemFile = (MemFile*)malloc (sizeof (MemFile));

    nSuccess = flex_reanchor ((flex_ptr)(& psMemFile->pcMemory),
      (flex_ptr)(ppcMem));

    psMemFile->nSize = nSize;

    if ((nSuccess == 0) || (psMemFile->pcMemory == NULL))
    {
      free (psMemFile);
      psMemFile = NULL;
      if (nSuccess == 0)
      {
        psMemFile = NULL;
//        ShowWarningTag ("Er20");
      }
    }
    else
    {
      if (psMemFile)
      {
//        if (strcmp (gszUnlockPassword, "") != 0)
//        {
//          // Attempt to unscramble the file
//          UnscrambleData ((int*)psMemFile->pcMemory,
//            (int*)psMemFile->pcMemory, ((psMemFile->nSize) / 4),
//            gszUnlockPassword);
//        }

        psMemFile->nPos = 0;
        psMemFile->cMode = mode[0];

        strncpy (psMemFile->szFilename, "", 1024);
      }
    }
  }

  if (mode[0] == 'w')
  {
    psMemFile = (MemFile*)malloc (sizeof (MemFile));

    nSuccess = flex_alloc ((flex_ptr)& psMemFile->pcMemory, MEMFILE_BLOCK);

    psMemFile->nPos = 0;
    psMemFile->cMode = mode[0];
    psMemFile->nSize = MEMFILE_BLOCK;

    strncpy (psMemFile->szFilename, "", 1024);

    if (nSuccess == 0)
    {
      free (psMemFile);
//      ShowWarningTag ("Er21");
      psMemFile = NULL;
    }
  }

  return psMemFile;
}

//////////////////////////////////////////////////////////////////
// Get the details of a memory 'file'
char * * meminfo (MemFile * psMemFile, int * pnPos)
{
  char                        **ppcReturn = NULL;

  if (psMemFile != NULL)
  {
    if (pnPos != NULL)
    {
      *pnPos = psMemFile->nPos;
    }

    ppcReturn = & psMemFile->pcMemory;
  }

  return ppcReturn;
}

